/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use app_units::Au;
use base::print_tree::PrintTree;
use serde::Serialize;
use servo_arc::Arc as ServoArc;
use style::properties::ComputedValues;

use super::{BaseFragment, BaseFragmentInfo, Fragment};
use crate::cell::ArcRefCell;
use crate::geom::PhysicalRect;

/// Can contain child fragments with relative coordinates, but does not contribute to painting
/// itself. [`PositioningFragment`]s may be completely anonymous, or just non-painting Fragments
/// generated by boxes.
#[derive(Serialize)]
pub(crate) struct PositioningFragment {
    pub base: BaseFragment,
    pub rect: PhysicalRect<Au>,
    pub children: Vec<ArcRefCell<Fragment>>,
    /// The scrollable overflow of this anonymous fragment's children.
    pub scrollable_overflow: PhysicalRect<Au>,

    /// If this fragment was created with a style, the style of the fragment.
    #[serde(skip_serializing)]
    pub style: Option<ServoArc<ComputedValues>>,
}

impl PositioningFragment {
    pub fn new_anonymous(rect: PhysicalRect<Au>, children: Vec<Fragment>) -> Self {
        Self::new_with_base_fragment(BaseFragment::anonymous(), None, rect, children)
    }

    pub fn new_empty(
        base_fragment_info: BaseFragmentInfo,
        rect: PhysicalRect<Au>,
        style: ServoArc<ComputedValues>,
    ) -> Self {
        Self::new_with_base_fragment(base_fragment_info.into(), Some(style), rect, Vec::new())
    }

    fn new_with_base_fragment(
        base: BaseFragment,
        style: Option<ServoArc<ComputedValues>>,
        rect: PhysicalRect<Au>,
        children: Vec<Fragment>,
    ) -> Self {
        let content_origin = rect.origin;
        let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
            acc.union(
                &child
                    .scrollable_overflow()
                    .translate(content_origin.to_vector()),
            )
        });
        PositioningFragment {
            base,
            style,
            rect,
            children: children.into_iter().map(ArcRefCell::new).collect(),
            scrollable_overflow,
        }
    }

    pub fn print(&self, tree: &mut PrintTree) {
        tree.new_level(format!(
            "PositioningFragment\
                \nbase={:?}\
                \nrect={:?}\
                \nscrollable_overflow={:?}",
            self.base, self.rect, self.scrollable_overflow
        ));

        for child in &self.children {
            child.borrow().print(tree);
        }
        tree.end_level();
    }
}
